import sys
import os
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QPushButton, QFileDialog, QSlider, QSpinBox, QComboBox,
                             QGroupBox, QGridLayout, QAction, QMenuBar, QSplitter, QMessageBox,
                             QScrollArea, QSizePolicy, QInputDialog)
from PyQt5.QtGui import QPixmap, QImage, QPainter, QPen, QColor, QCursor, QTransform
from PyQt5.QtCore import Qt, QPoint, QRect, QSize, QTimer, QUrl
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtMultimediaWidgets import QVideoWidget

# 导入OpenCV用于图像处理
import cv2
import threading
from pathlib import Path
import pickle


class ImageProcessingPlatform(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.current_image = None
        self.original_image = None
        self.temp_image = None
        self.shapes = []
        self.cropping = False
        self.crop_start = QPoint()
        self.crop_end = QPoint()
        self.images_to_stitch = []
        self.is_stitching = False

        # 人脸识别相关属性
        self.known_face_templates = []  # 存储人脸模板（OpenCV特征向量）
        self.known_face_names = []  # 存储人脸名称
        self.face_detector = None  # 人脸检测器
        self.init_face_detector()  # 初始化人脸检测器

        # 视频处理相关属性
        self.video_player = None  # 视频播放器
        self.video_widget = None  # 视频显示组件
        self.camera = None  # 摄像头对象
        self.is_camera_running = False  # 摄像头运行状态
        self.video_frame = None  # 视频帧缓存
        self.processing_lock = threading.Lock()  # 线程锁
        self.video_timer = QTimer(self)  # 视频帧处理定时器
        self.video_timer.timeout.connect(self.process_video_frame)

        # 替代视频播放器相关
        self.alternate_video_player = None
        self.alternate_video_active = False

        # 初始化视频组件
        self.init_video_components()

    def initUI(self):
        # 设置窗口标题和大小
        self.setWindowTitle('图像处理平台')
        self.setGeometry(100, 100, 1200, 800)

        # 创建菜单栏
        self.createMenus()

        # 创建主布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QHBoxLayout(central_widget)

        # 创建左侧控制面板
        control_panel = QWidget()
        control_layout = QVBoxLayout(control_panel)
        control_panel.setFixedWidth(300)

        # 图像加载组
        load_group = QGroupBox("图像加载")
        load_layout = QVBoxLayout(load_group)

        self.load_button = QPushButton("加载图像")
        self.load_button.clicked.connect(self.loadImage)

        self.stitch_button = QPushButton("添加到拼接")
        self.stitch_button.clicked.connect(self.addToStitch)
        self.stitch_button.setEnabled(False)

        self.perform_stitch_button = QPushButton("执行图像拼接")
        self.perform_stitch_button.clicked.connect(self.performStitch)
        self.perform_stitch_button.setEnabled(False)

        load_layout.addWidget(self.load_button)
        load_layout.addWidget(self.stitch_button)
        load_layout.addWidget(self.perform_stitch_button)

        # 图像编辑组
        edit_group = QGroupBox("图像编辑")
        edit_layout = QVBoxLayout(edit_group)

        # 旋转控件
        rotate_layout = QHBoxLayout()
        rotate_label = QLabel("旋转角度:")
        self.rotate_spin = QSpinBox()
        self.rotate_spin.setRange(-359, 359)
        self.rotate_button = QPushButton("旋转")
        self.rotate_button.clicked.connect(self.rotateImage)

        rotate_layout.addWidget(rotate_label)
        rotate_layout.addWidget(self.rotate_spin)
        rotate_layout.addWidget(self.rotate_button)

        # 缩放控件
        scale_layout = QHBoxLayout()
        scale_label = QLabel("缩放比例(%):")
        self.scale_spin = QSpinBox()
        self.scale_spin.setRange(10, 500)
        self.scale_spin.setValue(100)
        self.scale_button = QPushButton("缩放")
        self.scale_button.clicked.connect(self.scaleImage)

        scale_layout.addWidget(scale_label)
        scale_layout.addWidget(self.scale_spin)
        scale_layout.addWidget(self.scale_button)

        # 裁剪控件
        self.crop_button = QPushButton("裁剪图像")
        self.crop_button.clicked.connect(self.startCrop)

        edit_layout.addLayout(rotate_layout)
        edit_layout.addLayout(scale_layout)
        edit_layout.addWidget(self.crop_button)

        # 色彩调整组
        color_group = QGroupBox("色彩调整")
        color_layout = QGridLayout(color_group)

        # 亮度调整
        brightness_label = QLabel("亮度:")
        self.brightness_slider = QSlider(Qt.Horizontal)
        self.brightness_slider.setRange(-100, 100)
        self.brightness_slider.setValue(0)
        self.brightness_slider.valueChanged.connect(self.adjustColor)

        # 对比度调整
        contrast_label = QLabel("对比度:")
        self.contrast_slider = QSlider(Qt.Horizontal)
        self.contrast_slider.setRange(-100, 100)
        self.contrast_slider.setValue(0)
        self.contrast_slider.valueChanged.connect(self.adjustColor)

        # 饱和度调整
        saturation_label = QLabel("饱和度:")
        self.saturation_slider = QSlider(Qt.Horizontal)
        self.saturation_slider.setRange(-100, 100)
        self.saturation_slider.setValue(0)
        self.saturation_slider.valueChanged.connect(self.adjustColor)

        color_layout.addWidget(brightness_label, 0, 0)
        color_layout.addWidget(self.brightness_slider, 0, 1)
        color_layout.addWidget(contrast_label, 1, 0)
        color_layout.addWidget(self.contrast_slider, 1, 1)
        color_layout.addWidget(saturation_label, 2, 0)
        color_layout.addWidget(self.saturation_slider, 2, 1)

        # 平滑处理组
        smooth_group = QGroupBox("平滑处理")
        smooth_layout = QVBoxLayout(smooth_group)

        self.smooth_combo = QComboBox()
        self.smooth_combo.addItems(["均值滤波", "高斯滤波", "中值滤波", "双边滤波"])

        self.smooth_button = QPushButton("应用平滑")
        self.smooth_button.clicked.connect(self.applySmoothing)

        smooth_layout.addWidget(self.smooth_combo)
        smooth_layout.addWidget(self.smooth_button)

        # 形状识别组
        shape_group = QGroupBox("形状识别")
        shape_layout = QVBoxLayout(shape_group)

        self.shape_combo = QComboBox()
        self.shape_combo.addItems(["检测所有形状", "检测圆形", "检测矩形", "检测三角形"])

        self.shape_button = QPushButton("识别形状")
        self.shape_button.clicked.connect(self.detectShapes)

        shape_layout.addWidget(self.shape_combo)
        shape_layout.addWidget(self.shape_button)

        # 人脸识别组
        face_group = QGroupBox("人脸识别")
        face_layout = QVBoxLayout(face_group)

        # 注册按钮
        self.register_button = QPushButton("注册人脸")
        self.register_button.clicked.connect(self.register_face)

        # 识别按钮
        self.recognize_button = QPushButton("识别图像中的人脸")
        self.recognize_button.clicked.connect(self.recognize_faces)

        # 清空人脸库按钮
        self.clear_face_db_button = QPushButton("清空人脸库")
        self.clear_face_db_button.clicked.connect(self.clear_face_library)

        face_layout.addWidget(self.register_button)
        face_layout.addWidget(self.recognize_button)
        face_layout.addWidget(self.clear_face_db_button)

        # 视频控制组
        video_group = QGroupBox("视频处理")
        video_layout = QVBoxLayout(video_group)

        # 视频文件操作
        self.open_video_button = QPushButton("打开视频文件")
        self.open_video_button.clicked.connect(self.open_video_file)

        self.play_pause_button = QPushButton("播放")
        self.play_pause_button.clicked.connect(self.toggle_play_pause)
        self.play_pause_button.setEnabled(False)

        self.stop_button = QPushButton("停止")
        self.stop_button.clicked.connect(self.stop_video)
        self.stop_button.setEnabled(False)

        self.video_speed_spin = QSpinBox()
        self.video_speed_spin.setRange(1, 5)
        self.video_speed_spin.setValue(1)
        self.video_speed_spin.setSuffix("x")
        self.video_speed_spin.valueChanged.connect(self.set_video_speed)

        # 摄像头操作
        self.start_camera_button = QPushButton("开启摄像头")
        self.start_camera_button.clicked.connect(self.toggle_camera)

        self.process_camera_button = QPushButton("实时处理")
        self.process_camera_button.clicked.connect(self.toggle_camera_processing)
        self.process_camera_button.setEnabled(False)

        video_layout.addWidget(self.open_video_button)
        video_layout.addWidget(self.play_pause_button)
        video_layout.addWidget(self.stop_button)
        video_layout.addWidget(QLabel("播放速度:"))
        video_layout.addWidget(self.video_speed_spin)
        video_layout.addWidget(QLabel("摄像头控制:"))
        video_layout.addWidget(self.start_camera_button)
        video_layout.addWidget(self.process_camera_button)

        # 添加所有组到控制面板
        control_layout.addWidget(load_group)
        control_layout.addWidget(edit_group)
        control_layout.addWidget(color_group)
        control_layout.addWidget(smooth_group)
        control_layout.addWidget(shape_group)
        control_layout.addWidget(face_group)
        control_layout.addWidget(video_group)
        control_layout.addStretch()

        # 创建右侧图像/视频显示区域
        self.display_area = QWidget()
        self.display_layout = QVBoxLayout(self.display_area)

        # 图像显示标签
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)

        self.image_label = QLabel("请加载图像或视频")
        self.image_label.setAlignment(Qt.AlignCenter)
        self.image_label.setMinimumSize(1, 1)  # 允许标签缩小
        self.image_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.image_label.mousePressEvent = self.mousePress
        self.image_label.mouseMoveEvent = self.mouseMove
        self.image_label.mouseReleaseEvent = self.mouseRelease

        self.scroll_area.setWidget(self.image_label)

        # 视频显示组件
        self.video_display = QLabel("视频将在此处显示")
        self.video_display.setAlignment(Qt.AlignCenter)
        self.video_display.setMinimumSize(320, 240)
        self.video_display.setStyleSheet("border: 1px solid #cccccc;")

        self.display_layout.addWidget(self.scroll_area)
        self.display_layout.addWidget(self.video_display)

        # 添加到主布局
        main_layout.addWidget(control_panel)
        main_layout.addWidget(self.display_area, 1)

    def init_video_components(self):
        # 初始化视频播放器
        self.video_player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.video_player.positionChanged.connect(self.update_video_position)
        self.video_player.durationChanged.connect(self.update_video_duration)
        self.video_player.stateChanged.connect(self.video_state_changed)
        self.video_player.error.connect(self.handle_video_error)

        # 尝试创建视频widget，检查是否支持
        try:
            self.video_widget = QVideoWidget()
            # 测试是否能创建成功
            if self.video_widget:
                print("QVideoWidget创建成功，视频播放功能应该可用")
            else:
                print("QVideoWidget创建失败，视频播放可能不可用")
        except Exception as e:
            print(f"创建QVideoWidget时出错: {e}")
            self.video_widget = None

    def createMenus(self):
        # 创建文件菜单
        file_menu = self.menuBar().addMenu("文件")

        # 添加打开动作
        open_action = QAction("打开图像", self)
        open_action.setShortcut("Ctrl+O")
        open_action.triggered.connect(self.loadImage)
        file_menu.addAction(open_action)

        # 添加打开视频动作
        open_video_action = QAction("打开视频", self)
        open_video_action.setShortcut("Ctrl+V")
        open_video_action.triggered.connect(self.open_video_file)
        file_menu.addAction(open_video_action)

        # 添加保存动作
        save_action = QAction("保存", self)
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.saveImage)
        file_menu.addAction(save_action)

        # 添加保存人脸库动作
        save_face_action = QAction("保存人脸库", self)
        save_face_action.triggered.connect(self.save_face_library)
        file_menu.addAction(save_face_action)

        # 添加退出动作
        exit_action = QAction("退出", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 创建编辑菜单
        edit_menu = self.menuBar().addMenu("编辑")

        # 添加撤销动作
        undo_action = QAction("撤销", self)
        undo_action.setShortcut("Ctrl+Z")
        undo_action.triggered.connect(self.undo)
        edit_menu.addAction(undo_action)

        # 创建视图菜单
        view_menu = self.menuBar().addMenu("视图")

        # 添加适应窗口动作
        fit_action = QAction("适应窗口", self)
        fit_action.setShortcut("Ctrl+F")
        fit_action.triggered.connect(self.fitToWindow)
        view_menu.addAction(fit_action)

        # 添加实际大小动作
        actual_action = QAction("实际大小", self)
        actual_action.setShortcut("Ctrl+A")
        actual_action.triggered.connect(self.actualSize)
        view_menu.addAction(actual_action)

        # 创建视频菜单
        video_menu = self.menuBar().addMenu("视频")

        # 添加播放/暂停动作
        play_pause_action = QAction("播放/暂停", self)
        play_pause_action.setShortcut("Space")
        play_pause_action.triggered.connect(self.toggle_play_pause)
        video_menu.addAction(play_pause_action)

        # 添加停止动作
        stop_action = QAction("停止", self)
        stop_action.setShortcut("Ctrl+Space")
        stop_action.triggered.connect(self.stop_video)
        video_menu.addAction(stop_action)

        # 添加摄像头动作
        camera_action = QAction("开启/关闭摄像头", self)
        camera_action.setShortcut("Ctrl+C")
        camera_action.triggered.connect(self.toggle_camera)
        video_menu.addAction(camera_action)

    def init_face_detector(self):
        """初始化OpenCV人脸检测器"""
        try:
            # 加载预训练的人脸检测器
            self.face_detector = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
            if self.face_detector.empty():
                raise Exception("无法加载人脸检测器")
        except Exception as e:
            QMessageBox.critical(self, "初始化失败", f"加载人脸检测器时出错: {str(e)}")
            self.face_detector = None

    def loadImage(self):
        # 打开文件对话框选择图像
        file_name, _ = QFileDialog.getOpenFileName(
            self, "选择图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif)"
        )

        if file_name:
            # 保存原始图像
            self.original_image = cv2.imread(file_name)
            self.current_image = self.original_image.copy()
            self.temp_image = self.current_image.copy()
            self.shapes = []
            self.cropping = False
            self.images_to_stitch = []
            self.stitch_button.setEnabled(True)
            self.perform_stitch_button.setEnabled(False)
            self.updateImage()

            # 停止任何正在运行的视频或摄像头
            self.stop_video()
            self.stop_camera()

    def updateImage(self):
        if self.current_image is not None:
            # 转换OpenCV图像为Qt可显示格式
            height, width, channel = self.current_image.shape
            bytes_per_line = 3 * width
            q_img = QImage(self.current_image.data, width, height, bytes_per_line, QImage.Format_BGR888)

            # 如果有形状检测结果，绘制它们
            if self.shapes:
                painter = QPainter(q_img)
                painter.setRenderHint(QPainter.Antialiasing)

                for shape in self.shapes:
                    shape_type, contour = shape
                    if shape_type == "圆形":
                        # 绘制圆形
                        (x, y), radius = cv2.minEnclosingCircle(contour)
                        center = (int(x), int(y))
                        radius = int(radius)
                        painter.setPen(QPen(QColor(255, 0, 0), 2))
                        painter.drawEllipse(center[0] - radius, center[1] - radius, radius * 2, radius * 2)
                        painter.drawText(center[0] - 10, center[1] - radius - 10, "圆形")
                    elif shape_type == "矩形":
                        # 绘制矩形
                        x, y, w, h = cv2.boundingRect(contour)
                        painter.setPen(QPen(QColor(0, 255, 0), 2))
                        painter.drawRect(x, y, w, h)
                        painter.drawText(x, y - 10, "矩形")
                    elif shape_type == "三角形":
                        # 绘制三角形
                        epsilon = 0.02 * cv2.arcLength(contour, True)
                        approx = cv2.approxPolyDP(contour, epsilon, True)
                        if len(approx) == 3:
                            points = [(p[0][0], p[0][1]) for p in approx]
                            painter.setPen(QPen(QColor(0, 0, 255), 2))
                            for i in range(3):
                                painter.drawLine(points[i][0], points[i][1], points[(i + 1) % 3][0],
                                                 points[(i + 1) % 3][1])
                            painter.drawText(points[0][0], points[0][1] - 10, "三角形")
                    elif shape_type == "多边形":
                        # 绘制多边形
                        epsilon = 0.02 * cv2.arcLength(contour, True)
                        approx = cv2.approxPolyDP(contour, epsilon, True)
                        if len(approx) > 4:
                            points = [(p[0][0], p[0][1]) for p in approx]
                            painter.setPen(QPen(QColor(255, 255, 0), 2))
                            for i in range(len(points)):
                                painter.drawLine(points[i][0], points[i][1], points[(i + 1) % len(points)][0],
                                                 points[(i + 1) % len(points)][1])
                            painter.drawText(points[0][0], points[0][1] - 10, f"多边形({len(points)}边)")

                painter.end()

            # 如果正在裁剪，绘制裁剪框
            if self.cropping and not self.crop_start.isNull() and not self.crop_end.isNull():
                painter = QPainter(q_img)
                painter.setPen(QPen(QColor(255, 0, 0), 2, Qt.DashLine))

                # 确保坐标是正确的顺序
                x = min(self.crop_start.x(), self.crop_end.x())
                y = min(self.crop_start.y(), self.crop_end.y())
                w = abs(self.crop_start.x() - self.crop_end.x())
                h = abs(self.crop_start.y() - self.crop_end.y())

                painter.drawRect(x, y, w, h)
                painter.end()

            # 如果有人脸识别结果，绘制人脸框和标签
            if hasattr(self, 'face_locations') and self.face_locations:
                painter = QPainter(q_img)
                painter.setRenderHint(QPainter.Antialiasing)

                for (x, y, w, h), name, similarity in zip(self.face_locations, self.face_names, self.face_similarities):
                    # 绘制人脸框
                    painter.setPen(QPen(QColor(255, 0, 255), 2))
                    painter.drawRect(x, y, w, h)

                    # 绘制名字和相似度
                    text = f"{name}: {similarity:.2f}%"
                    painter.fillRect(x, y - 20, w, 20, QColor(255, 0, 255))
                    painter.setPen(QPen(QColor(255, 255, 255), 1))
                    painter.drawText(x + 5, y - 5, text)

                painter.end()

            # 显示图像
            pixmap = QPixmap.fromImage(q_img)
            self.image_label.setPixmap(pixmap.scaled(
                self.image_label.size(),
                Qt.KeepAspectRatio,
                Qt.SmoothTransformation
            ))

    def saveImage(self):
        if self.current_image is not None:
            file_name, _ = QFileDialog.getSaveFileName(
                self, "保存图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp)"
            )

            if file_name:
                try:
                    cv2.imwrite(file_name, self.current_image)
                    QMessageBox.information(self, "成功", "图像保存成功!")
                except Exception as e:
                    QMessageBox.critical(self, "错误", f"保存图像时出错: {str(e)}")

    def undo(self):
        if self.original_image is not None:
            self.current_image = self.original_image.copy()
            self.temp_image = self.current_image.copy()
            self.shapes = []
            self.cropping = False
            # 清除人脸识别结果
            if hasattr(self, 'face_locations'):
                self.face_locations = []
                self.face_names = []
                self.face_similarities = []
            self.updateImage()

    def fitToWindow(self):
        if not self.image_label.pixmap().isNull():
            self.image_label.adjustSize()

    def actualSize(self):
        if not self.image_label.pixmap().isNull():
            self.image_label.setPixmap(self.image_label.pixmap().scaled(
                self.current_image.shape[1],
                self.current_image.shape[0],
                Qt.KeepAspectRatio,
                Qt.SmoothTransformation
            ))

    def rotateImage(self):
        if self.current_image is not None:
            angle = self.rotate_spin.value()
            if angle != 0:
                rows, cols = self.current_image.shape[:2]
                M = cv2.getRotationMatrix2D((cols / 2, rows / 2), angle, 1)
                self.current_image = cv2.warpAffine(self.current_image, M, (cols, rows))
                self.temp_image = self.current_image.copy()
                # 清除人脸识别结果
                if hasattr(self, 'face_locations'):
                    self.face_locations = []
                    self.face_names = []
                    self.face_similarities = []
                self.updateImage()

    def scaleImage(self):
        if self.current_image is not None:
            scale_percent = self.scale_spin.value()
            width = int(self.current_image.shape[1] * scale_percent / 100)
            height = int(self.current_image.shape[0] * scale_percent / 100)
            dim = (width, height)
            self.current_image = cv2.resize(self.current_image, dim, interpolation=cv2.INTER_AREA)
            self.temp_image = self.current_image.copy()
            # 清除人脸识别结果
            if hasattr(self, 'face_locations'):
                self.face_locations = []
                self.face_names = []
                self.face_similarities = []
            self.updateImage()

    def startCrop(self):
        if self.current_image is not None:
            self.cropping = True
            self.crop_start = QPoint()
            self.crop_end = QPoint()
            self.setCursor(Qt.CrossCursor)
            self.updateImage()

    def mousePress(self, event):
        if self.cropping and event.button() == Qt.LeftButton:
            self.crop_start = event.pos()
            self.crop_end = QPoint()
            self.updateImage()

    def mouseMove(self, event):
        if self.cropping and (event.buttons() & Qt.LeftButton):
            self.crop_end = event.pos()
            self.updateImage()

    def mouseRelease(self, event):
        if self.cropping and event.button() == Qt.LeftButton:
            self.crop_end = event.pos()
            self.setCursor(Qt.ArrowCursor)

            # 确保坐标是正确的顺序
            x = min(self.crop_start.x(), self.crop_end.x())
            y = min(self.crop_start.y(), self.crop_end.y())
            w = abs(self.crop_start.x() - self.crop_end.x())
            h = abs(self.crop_start.y() - self.crop_end.y())

            if w > 0 and h > 0:
                # 裁剪图像
                if not self.image_label.pixmap().isNull():
                    pixmap = self.image_label.pixmap()
                    cropped_pixmap = pixmap.copy(x, y, w, h)

                    # 转换回OpenCV格式
                    qimg = cropped_pixmap.toImage()
                    ptr = qimg.bits()
                    ptr.setsize(qimg.byteCount())
                    arr = np.array(ptr).reshape(qimg.height(), qimg.width(), 3)
                    self.current_image = cv2.cvtColor(arr, cv2.COLOR_RGB2BGR)
                    self.temp_image = self.current_image.copy()
                    self.cropping = False
                    # 清除人脸识别结果
                    if hasattr(self, 'face_locations'):
                        self.face_locations = []
                        self.face_names = []
                        self.face_similarities = []
                    self.updateImage()

    def adjustColor(self):
        if self.current_image is not None:
            brightness = self.brightness_slider.value()
            contrast = self.contrast_slider.value()
            saturation = self.saturation_slider.value()

            # 创建临时图像进行调整
            img = self.temp_image.copy()

            # 调整亮度和对比度
            if brightness != 0 or contrast != 0:
                alpha = 1.0 + contrast / 100.0
                beta = brightness
                img = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)

            # 调整饱和度
            if saturation != 0:
                hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
                h, s, v = cv2.split(hsv)

                if saturation > 0:
                    limit = 255 - saturation
                    s[s > limit] = 255
                    s[s <= limit] += saturation
                else:
                    s = np.maximum(s, np.zeros(s.shape) - saturation)
                    s = s + saturation

                hsv = cv2.merge([h, s, v])
                img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

            self.current_image = img
            # 清除人脸识别结果
            if hasattr(self, 'face_locations'):
                self.face_locations = []
                self.face_names = []
                self.face_similarities = []
            self.updateImage()

    def applySmoothing(self):
        if self.current_image is not None:
            method = self.smooth_combo.currentText()
            img = self.current_image.copy()

            if method == "均值滤波":
                img = cv2.blur(img, (5, 5))
            elif method == "高斯滤波":
                img = cv2.GaussianBlur(img, (5, 5), 0)
            elif method == "中值滤波":
                img = cv2.medianBlur(img, 5)
            elif method == "双边滤波":
                img = cv2.bilateralFilter(img, 9, 75, 75)

            self.current_image = img
            self.temp_image = self.current_image.copy()
            # 清除人脸识别结果
            if hasattr(self, 'face_locations'):
                self.face_locations = []
                self.face_names = []
                self.face_similarities = []
            self.updateImage()

    def detectShapes(self):
        if self.current_image is not None:
            method = self.shape_combo.currentText()
            img = self.current_image.copy()
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            blurred = cv2.GaussianBlur(gray, (5, 5), 0)
            _, thresh = cv2.threshold(blurred, 127, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

            # 查找轮廓
            contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

            self.shapes = []

            for contour in contours:
                area = cv2.contourArea(contour)
                # 忽略小面积区域
                if area < 100:
                    continue

                if method == "检测所有形状" or method == "检测圆形":
                    # 检测圆形
                    perimeter = cv2.arcLength(contour, True)
                    approx = cv2.approxPolyDP(contour, 0.04 * perimeter, True)
                    (x, y), radius = cv2.minEnclosingCircle(contour)
                    circle_area = np.pi * radius * radius

                    # 如果面积比例接近圆形，认为是圆形
                    if abs(area - circle_area) / circle_area < 0.3:
                        self.shapes.append(("圆形", contour))
                        if method == "检测圆形":
                            continue

                if method == "检测所有形状" or method == "检测矩形":
                    # 检测矩形
                    perimeter = cv2.arcLength(contour, True)
                    approx = cv2.approxPolyDP(contour, 0.04 * perimeter, True)

                    if len(approx) == 4:
                        self.shapes.append(("矩形", contour))
                        if method == "检测矩形":
                            continue

                if method == "检测所有形状" or method == "检测三角形":
                    # 检测三角形
                    perimeter = cv2.arcLength(contour, True)
                    approx = cv2.approxPolyDP(contour, 0.02 * perimeter, True)

                    if len(approx) == 3:
                        self.shapes.append(("三角形", contour))
                        if method == "检测三角形":
                            continue

                # 如果是检测所有形状且未匹配到其他形状，认为是多边形
                if method == "检测所有形状":
                    self.shapes.append(("多边形", contour))

            self.updateImage()

    def addToStitch(self):
        if self.current_image is not None:
            self.images_to_stitch.append(self.current_image.copy())
            QMessageBox.information(self, "成功", f"已添加图像到拼接队列，当前共有 {len(self.images_to_stitch)} 张图像")
            self.perform_stitch_button.setEnabled(len(self.images_to_stitch) >= 2)

    def performStitch(self):
        if len(self.images_to_stitch) < 2:
            QMessageBox.warning(self, "警告", "至少需要两张图像才能进行拼接")
            return

        # 创建图像拼接器
        stitcher = cv2.Stitcher_create() if hasattr(cv2, 'Stitcher_create') else cv2.Stitcher_create(cv2.Stitcher_SCANS)

        # 执行拼接
        status, stitched = stitcher.stitch(self.images_to_stitch)

        if status == cv2.Stitcher_OK:
            self.current_image = stitched
            self.temp_image = self.current_image.copy()
            self.shapes = []
            # 清除人脸识别结果
            if hasattr(self, 'face_locations'):
                self.face_locations = []
                self.face_names = []
                self.face_similarities = []
            self.updateImage()
            QMessageBox.information(self, "成功", "图像拼接成功!")
        else:
            QMessageBox.critical(self, "错误", f"图像拼接失败，错误码: {status}")

    # ===================== 人脸识别功能模块 =====================
    def save_face_library(self):
        """保存人脸库数据到文件"""
        if not self.known_face_templates:
            QMessageBox.warning(self, "人脸库保存", "人脸库为空，无需保存")
            return

        face_lib_path = Path("face_library.pkl")
        try:
            data = {
                'templates': self.known_face_templates,
                'names': self.known_face_names
            }
            with open(face_lib_path, 'wb') as f:
                pickle.dump(data, f)
            QMessageBox.information(self, "人脸库保存", f"成功保存 {len(self.known_face_names)} 个人脸模板")
        except Exception as e:
            QMessageBox.critical(self, "人脸库保存失败", f"保存人脸库时出错: {str(e)}")

    def load_face_library(self):
        """加载预存的人脸库数据"""
        face_lib_path = Path("face_library.pkl")
        if face_lib_path.exists():
            try:
                with open(face_lib_path, 'rb') as f:
                    data = pickle.load(f)
                    self.known_face_templates = data['templates']
                    self.known_face_names = data['names']
                QMessageBox.information(self, "人脸库加载", f"成功加载 {len(self.known_face_names)} 个人脸模板")
            except Exception as e:
                QMessageBox.warning(self, "人脸库加载失败", f"加载人脸库时出错: {str(e)}")
        else:
            QMessageBox.information(self, "人脸库", "未找到人脸库文件，将使用空库")

    def clear_face_library(self):
        """清空人脸库"""
        reply = QMessageBox.question(
            self, "确认清空", "确定要清空所有人脸模板吗？此操作不可撤销。",
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No
        )
        if reply == QMessageBox.Yes:
            self.known_face_templates = []
            self.known_face_names = []
            face_lib_path = Path("face_library.pkl")
            if face_lib_path.exists():
                face_lib_path.unlink()
            QMessageBox.information(self, "人脸库已清空", "所有人脸模板已删除")

    def register_face(self):
        """注册当前图像中的人脸"""
        if self.current_image is None:
            QMessageBox.warning(self, "注册人脸", "请先加载图像")
            return

        if self.face_detector is None:
            QMessageBox.warning(self, "注册人脸", "人脸检测器未初始化")
            return

        # 转换为灰度图
        gray = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2GRAY)

        # 检测人脸
        faces = self.face_detector.detectMultiScale(gray, 1.1, 4)

        if len(faces) == 0:
            QMessageBox.warning(self, "注册人脸", "未检测到人脸")
            return

        if len(faces) > 1:
            QMessageBox.warning(self, "注册人脸", f"检测到{len(faces)}个人脸，请确保图像中只有一个人脸")
            return

        # 获取人脸区域
        (x, y, w, h) = faces[0]

        # 提取人脸ROI并调整大小
        face_roi = gray[y:y + h, x:x + w]
        face_roi = cv2.resize(face_roi, (100, 100))

        # 获取用户输入的人脸名称
        name, ok = QInputDialog.getText(self, "注册人脸", "请输入此人的姓名:")
        if not ok or not name:
            return

        # 使用LBPH人脸识别器创建人脸模板
        recognizer = cv2.face.LBPHFaceRecognizer_create()
        recognizer.train([face_roi], np.array([0]))

        # 保存人脸模板和名称
        self.known_face_templates.append(recognizer)
        self.known_face_names.append(name)

        QMessageBox.information(self, "注册成功", f"已成功将 '{name}' 注册到人脸库")

    def recognize_faces(self):
        """识别当前图像中的人脸"""
        if self.current_image is None:
            QMessageBox.warning(self, "人脸识别", "请先加载图像")
            return

        if not self.known_face_templates:
            QMessageBox.warning(self, "人脸识别", "人脸库为空，请先注册人脸")
            return

        if self.face_detector is None:
            QMessageBox.warning(self, "人脸识别", "人脸检测器未初始化")
            return

        # 转换为灰度图
        gray = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2GRAY)

        # 检测人脸
        faces = self.face_detector.detectMultiScale(gray, 1.1, 4)

        self.face_locations = faces
        self.face_names = []
        self.face_similarities = []

        if len(faces) == 0:
            QMessageBox.information(self, "人脸识别", "未检测到人脸")
            self.updateImage()
            return

        # 对每个检测到的人脸进行识别
        for (x, y, w, h) in faces:
            # 提取人脸ROI并调整大小
            face_roi = gray[y:y + h, x:x + w]
            face_roi = cv2.resize(face_roi, (100, 100))

            best_match_index = -1
            best_confidence = float('inf')  # LBPH使用距离，越小越相似

            # 在所有人脸模板中查找最佳匹配
            for i, recognizer in enumerate(self.known_face_templates):
                label, confidence = recognizer.predict(face_roi)

                if confidence < best_confidence:
                    best_confidence = confidence
                    best_match_index = i

            # 将距离转换为相似度百分比 (距离越小，相似度越高)
            similarity = max(0, 100 - best_confidence)

            if similarity > 50:  # 设置相似度阈值
                name = self.known_face_names[best_match_index]
            else:
                name = "未知"

            self.face_names.append(name)
            self.face_similarities.append(similarity)

        # 更新图像显示
        self.updateImage()

        # 显示识别结果摘要
        result_text = f"共识别到 {len(faces)} 个人脸:\n"
        for name, similarity in zip(self.face_names, self.face_similarities):
            result_text += f"- {name}: {similarity:.2f}%\n"

        QMessageBox.information(self, "人脸识别结果", result_text)

    # ===================== 视频处理功能模块 =====================
    def open_video_file(self):
        """打开视频文件"""
        file_name, _ = QFileDialog.getOpenFileName(
            self, "选择视频", "", "视频文件 (*.mp4 *.avi *.mov *.mkv *.wmv)"
        )

        if file_name:
            # 停止任何正在运行的视频或摄像头
            self.stop_video()
            self.stop_camera()

            # 尝试使用Qt的视频播放器
            self.video_player.setMedia(QMediaContent(QUrl.fromLocalFile(file_name)))
            self.video_player.setVideoOutput(self.video_display)

            # 尝试播放
            self.play_pause_button.setEnabled(True)
            self.stop_button.setEnabled(True)
            self.video_player.play()

    def toggle_play_pause(self):
        """播放/暂停视频"""
        if self.video_player.state() == QMediaPlayer.PlayingState:
            self.video_player.pause()
            self.play_pause_button.setText("播放")
        else:
            self.video_player.play()
            self.play_pause_button.setText("暂停")

    def stop_video(self):
        """停止视频播放"""
        self.video_player.stop()
        self.play_pause_button.setText("播放")
        self.play_pause_button.setEnabled(False)
        self.stop_button.setEnabled(False)

    def set_video_speed(self):
        """设置视频播放速度"""
        speed = self.video_speed_spin.value()
        self.video_player.setPlaybackRate(speed)

    def update_video_position(self, position):
        """更新视频播放位置"""
        # 可添加进度条更新逻辑
        pass

    def update_video_duration(self, duration):
        """更新视频总时长"""
        # 可添加进度条更新逻辑
        pass

    def video_state_changed(self, state):
        """视频状态变化处理"""
        if state == QMediaPlayer.PlayingState:
            self.play_pause_button.setText("暂停")
        else:
            self.play_pause_button.setText("播放")

    def handle_video_error(self, error):
        """处理视频播放错误"""
        error_str = ""
        if error == QMediaPlayer.NoError:
            error_str = "无错误"
        elif error == QMediaPlayer.ResourceError:
            error_str = "资源无法被解析"
        elif error == QMediaPlayer.FormatError:
            error_str = "不支持的格式"
        elif error == QMediaPlayer.NetworkError:
            error_str = "网络错误"
        elif error == QMediaPlayer.AccessDeniedError:
            error_str = "没有访问权限"
        elif error == QMediaPlayer.ServiceMissingError:
            error_str = "没有找到必要的播放服务"
        else:
            error_str = "未知错误"

        QMessageBox.critical(self, "视频播放错误", f"无法播放视频: {error_str}\n\n"
                                             "可能的原因:\n"
                                             "1. 您的系统缺少必要的多媒体组件\n"
                                             "2. 视频格式不支持\n"
                                             "3. 视频文件已损坏\n\n"
                                             "您可以尝试使用OpenCV替代播放器。")

        # 尝试使用OpenCV作为替代播放器
        self.try_opencv_player()

    def try_opencv_player(self):
        """尝试使用OpenCV作为替代视频播放器"""
        if self.video_player.mediaStatus() != QMediaPlayer.NoMedia:
            media = self.video_player.media()
            url = media.canonicalUrl()
            file_path = url.toLocalFile()

            if file_path:
                # 尝试使用OpenCV打开视频
                try:
                    self.alternate_video_player = cv2.VideoCapture(file_path)
                    if self.alternate_video_player.isOpened():
                        self.alternate_video_active = True
                        self.video_timer.start(30)  # 开始定时更新帧
                        QMessageBox.information(self, "替代播放器", "已成功使用OpenCV播放器打开视频")
                    else:
                        QMessageBox.critical(self, "OpenCV播放失败", "无法使用OpenCV打开视频文件")
                except Exception as e:
                    QMessageBox.critical(self, "OpenCV播放错误", f"使用OpenCV播放视频时出错: {str(e)}")

    def toggle_camera(self):
        """开启/关闭摄像头"""
        if not self.is_camera_running:
            # 开启摄像头
            self.camera = cv2.VideoCapture(0)
            if not self.camera.isOpened():
                QMessageBox.critical(self, "错误", "无法打开摄像头")
                return

            self.is_camera_running = True
            self.start_camera_button.setText("关闭摄像头")
            self.process_camera_button.setEnabled(True)
            self.video_timer.start(30)  # 约30fps
        else:
            # 关闭摄像头
            self.stop_camera()

    def stop_camera(self):
        """停止摄像头"""
        if self.is_camera_running:
            self.is_camera_running = False
            self.start_camera_button.setText("开启摄像头")
            self.process_camera_button.setText("实时处理")
            self.process_camera_button.setEnabled(False)
            self.video_timer.stop()
            if self.camera:
                self.camera.release()
                self.camera = None

    def toggle_camera_processing(self):
        """切换是否对摄像头画面进行实时处理"""
        if self.process_camera_button.text() == "实时处理":
            self.process_camera_button.setText("停止处理")
        else:
            self.process_camera_button.setText("实时处理")

    def process_video_frame(self):
        """处理视频帧（摄像头或视频文件）"""
        if self.is_camera_running:
            # 处理摄像头帧
            ret, frame = self.camera.read()
            if not ret:
                return
        elif self.alternate_video_active and self.alternate_video_player:
            # 处理替代视频播放器的帧
            ret, frame = self.alternate_video_player.read()
            if not ret:
                # 视频结束，重置到开始
                self.alternate_video_player.set(cv2.CAP_PROP_POS_FRAMES, 0)
                return
        else:
            return

        # 保存当前帧
        self.video_frame = frame.copy()

        # 检查是否需要处理
        if self.process_camera_button.text() == "停止处理":
            # 应用当前选择的处理效果
            if self.shape_combo.currentText() != "不检测形状":
                # 形状检测
                method = self.shape_combo.currentText()
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                blurred = cv2.GaussianBlur(gray, (5, 5), 0)
                _, thresh = cv2.threshold(blurred, 127, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

                contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

                for contour in contours:
                    area = cv2.contourArea(contour)
                    if area < 100:
                        continue

                    if method == "检测所有形状" or method == "检测圆形":
                        perimeter = cv2.arcLength(contour, True)
                        approx = cv2.approxPolyDP(contour, 0.04 * perimeter, True)
                        (x, y), radius = cv2.minEnclosingCircle(contour)
                        circle_area = np.pi * radius * radius

                        if abs(area - circle_area) / circle_area < 0.3:
                            cv2.circle(frame, (int(x), int(y)), int(radius), (0, 0, 255), 2)
                            cv2.putText(frame, "圆形", (int(x) - 10, int(y) - int(radius) - 10),
                                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
                            if method == "检测圆形":
                                continue

                    if method == "检测所有形状" or method == "检测矩形":
                        perimeter = cv2.arcLength(contour, True)
                        approx = cv2.approxPolyDP(contour, 0.04 * perimeter, True)

                        if len(approx) == 4:
                            x, y, w, h = cv2.boundingRect(contour)
                            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
                            cv2.putText(frame, "矩形", (x, y - 10),
                                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
                            if method == "检测矩形":
                                continue

                    if method == "检测所有形状" or method == "检测三角形":
                        perimeter = cv2.arcLength(contour, True)
                        approx = cv2.approxPolyDP(contour, 0.02 * perimeter, True)

                        if len(approx) == 3:
                            pts = [(p[0][0], p[0][1]) for p in approx]
                            for i in range(3):
                                cv2.line(frame, pts[i], pts[(i + 1) % 3], (255, 0, 0), 2)
                            cv2.putText(frame, "三角形", (pts[0][0], pts[0][1] - 10),
                                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
                            if method == "检测三角形":
                                continue

                    if method == "检测所有形状":
                        cv2.drawContours(frame, [contour], -1, (255, 255, 0), 2)
                        cv2.putText(frame, f"多边形({len(approx)})", (approx[0][0][0], approx[0][0][1] - 10),
                                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)

            # 人脸识别
            if self.recognize_button.isEnabled() and self.known_face_templates:
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                faces = self.face_detector.detectMultiScale(gray, 1.1, 4)

                for (x, y, w, h) in faces:
                    face_roi = gray[y:y + h, x:x + w]
                    face_roi = cv2.resize(face_roi, (100, 100))

                    best_match_index = -1
                    best_confidence = float('inf')

                    for i, recognizer in enumerate(self.known_face_templates):
                        label, confidence = recognizer.predict(face_roi)
                        if confidence < best_confidence:
                            best_confidence = confidence
                            best_match_index = i

                    similarity = max(0, 100 - best_confidence)

                    if similarity > 50:
                        name = self.known_face_names[best_match_index]
                    else:
                        name = "未知"

                    cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 255), 2)
                    text = f"{name}: {similarity:.2f}%"
                    cv2.rectangle(frame, (x, y - 20), (x + w, y), (255, 0, 255), -1)
                    cv2.putText(frame, text, (x + 5, y - 5),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)

            # 色彩调整
            brightness = self.brightness_slider.value()
            contrast = self.contrast_slider.value()
            saturation = self.saturation_slider.value()

            if brightness != 0 or contrast != 0:
                alpha = 1.0 + contrast / 100.0
                beta = brightness
                frame = cv2.convertScaleAbs(frame, alpha=alpha, beta=beta)

            if saturation != 0:
                hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
                h, s, v = cv2.split(hsv)

                if saturation > 0:
                    limit = 255 - saturation
                    s[s > limit] = 255
                    s[s <= limit] += saturation
                else:
                    s = np.maximum(s, np.zeros(s.shape) - saturation)
                    s = s + saturation

                hsv = cv2.merge([h, s, v])
                frame = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

            # 平滑处理
            if self.smooth_combo.currentText() != "不应用平滑":
                method = self.smooth_combo.currentText()
                if method == "均值滤波":
                    frame = cv2.blur(frame, (5, 5))
                elif method == "高斯滤波":
                    frame = cv2.GaussianBlur(frame, (5, 5), 0)
                elif method == "中值滤波":
                    frame = cv2.medianBlur(frame, 5)
                elif method == "双边滤波":
                    frame = cv2.bilateralFilter(frame, 9, 75, 75)

        # 显示处理后的帧
        height, width, channel = frame.shape
        bytes_per_line = 3 * width
        q_img = QImage(frame.data, width, height, bytes_per_line, QImage.Format_BGR888)
        pixmap = QPixmap.fromImage(q_img)
        self.image_label.setPixmap(pixmap.scaled(
            self.image_label.size(),
            Qt.KeepAspectRatio,
            Qt.SmoothTransformation
        ))

    def closeEvent(self, event):
        """
        窗口关闭时释放资源
        """
        self.stop_video()
        self.stop_camera()
        if self.alternate_video_player:
            self.alternate_video_player.release()
        event.accept()


if __name__ == '__main__':
    # 创建Qt应用
    app = QApplication(sys.argv)

    # 确保中文显示正常
    font = app.font()
    font.setFamily("SimHei")
    app.setFont(font)

    # 创建并显示主窗口
    window = ImageProcessingPlatform()
    window.show()

    # 进入应用主循环
    sys.exit(app.exec_())